// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/slide_out_view.h"

#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/transform.h"

namespace views {

SlideOutView::SlideOutView()
    : gesture_scroll_amount_(0.f)
{
    // If accelerated compositing is not available, this widget tracks the
    // OnSlideOut event but does not render any visible changes.
    SetPaintToLayer(true);
    SetFillsBoundsOpaquely(false);
}

SlideOutView::~SlideOutView()
{
}

void SlideOutView::OnGestureEvent(ui::GestureEvent* event)
{
    if (event->type() == ui::ET_SCROLL_FLING_START) {
        // The threshold for the fling velocity is computed empirically.
        // The unit is in pixels/second.
        const float kFlingThresholdForClose = 800.f;
        if (fabsf(event->details().velocity_x()) > kFlingThresholdForClose) {
            SlideOutAndClose(event->details().velocity_x() < 0 ? SLIDE_LEFT : SLIDE_RIGHT);
            event->StopPropagation();
            return;
        }
        RestoreVisualState();
        return;
    }

    if (!event->IsScrollGestureEvent())
        return;

    if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
        gesture_scroll_amount_ = 0.f;
    } else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
        // The scroll-update events include the incremental scroll amount.
        gesture_scroll_amount_ += event->details().scroll_x();

        gfx::Transform transform;
        transform.Translate(gesture_scroll_amount_, 0.0);
        layer()->SetTransform(transform);
        layer()->SetOpacity(
            1.f - std::min(fabsf(gesture_scroll_amount_) / width(), 1.f));

    } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
        const float kScrollRatioForClosingNotification = 0.5f;
        float scrolled_ratio = fabsf(gesture_scroll_amount_) / width();
        if (scrolled_ratio >= kScrollRatioForClosingNotification) {
            SlideOutAndClose(gesture_scroll_amount_ < 0 ? SLIDE_LEFT : SLIDE_RIGHT);
            event->StopPropagation();
            return;
        }
        RestoreVisualState();
    }

    event->SetHandled();
}

void SlideOutView::RestoreVisualState()
{
    // Restore the layer state.
    const int kSwipeRestoreDurationMS = 150;
    ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
    settings.SetTransitionDuration(
        base::TimeDelta::FromMilliseconds(kSwipeRestoreDurationMS));
    layer()->SetTransform(gfx::Transform());
    layer()->SetOpacity(1.f);
}

void SlideOutView::SlideOutAndClose(SlideDirection direction)
{
    const int kSwipeOutTotalDurationMS = 150;
    int swipe_out_duration = kSwipeOutTotalDurationMS * layer()->opacity();
    ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
    settings.SetTransitionDuration(
        base::TimeDelta::FromMilliseconds(swipe_out_duration));
    settings.AddObserver(this);

    gfx::Transform transform;
    transform.Translate(direction == SLIDE_LEFT ? -width() : width(), 0.0);
    layer()->SetTransform(transform);
    layer()->SetOpacity(0.f);
}

void SlideOutView::OnImplicitAnimationsCompleted()
{
    OnSlideOut();
}

} // namespace views
